﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using Zendesk.NET.Models;

namespace Zendesk.NET
{
    public class Client
    {
        private readonly string _site;
        private readonly string _username;
        private readonly string _password;

        public Client(string site, string username, string password)
        {
            _site = site;
            _username = username;
            _password = password;
        }

        public Ticket GetTicket(int id)
        {
            return Show<Ticket>("tickets", id);
        }

        public void UpdateTicket(int id, Comment comment)
        {
            Update("tickets", id, comment);
        }



        public OrganizationCollection GetOrganizations()
        {
            return List<OrganizationCollection>("organizations");
        }

        public Organization GetOrganization(int id)
        {
            return Show<Organization>("organizations", id);
        }

        public void UpdateOrganization(Organization organization)
        {
            Update("organizations", organization.Id, organization);
        }

        public CategoryCollection GetCategories()
        {
            return List<CategoryCollection>("categories");
        }

        public Category GetCategory(int id)
        {
            return Show<Category>("categories", id);
        }

        public ForumCollection GetForums()
        {
            return List<ForumCollection>("forums");
        }

        public Forum GetForum(int id)
        {
            return Show<Forum>("forums", id);
        }

        public EntryCollection GetEntries()
        {
            return List<EntryCollection>("entries");
        }

        public EntryCollection GetEntries(int forumId)
        {
            return ProcessGetRequest<EntryCollection>(String.Format("http://{0}.zendesk.com/forums/{1}/entries.xml", _site,
                                                                forumId));
        }

        public Entry GetEntry(int id)
        {
            return Show<Entry>("entries", id);
        }

        public PostCollection GetPosts(int entryId)
        {
            return
                ProcessGetRequest<PostCollection>(String.Format("http://{0}.zendesk.com/entries/{1}/posts.xml", _site,
                                                                entryId));
        }



        private T List<T>(string endpoint)
        {
            return ProcessGetRequest<T>(String.Format("http://{0}.zendesk.com/{1}.xml", _site, endpoint));
        }

        private T Show<T>(string endpoint, int id)
        {
            return ProcessGetRequest<T>(String.Format("http://{0}.zendesk.com/{1}/{2}.xml", _site, endpoint, id));
        }

        private void Update<T>(string endpoint, int id, T item)
        {
            ProcessPutRequest(endpoint, id, item);
        }

        private T ProcessGetRequest<T>(string url)
        {
            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.Credentials = new NetworkCredential(_username, _password);
            request.ContentType = "application/xml";
            request.Timeout = 10000;

            try
            {
                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    using (var r = response.GetResponseStream())
                    {
                        //using (var reader = new StreamReader(r))
                        //{
                        var d = new XmlDocument();
                        d.Load(r);
                        foreach (XmlNode childNode in d.ChildNodes)
                        {
                            FixNilAttributeName(childNode);
                        }


                        var serializer = new XmlSerializer(typeof(T));
                        var ticket = (T)serializer.Deserialize(XmlReader.Create(new StringReader(d.InnerXml)));
                        //reader.Close();

                        return ticket;
                        //}
                    }
                }

            }
            catch (Exception ex)
            {
                return default(T);
            }
        }

        private void ProcessPutRequest<T>(string endpoint, int id, T value)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(Serialize(value));


            var request = (HttpWebRequest)WebRequest.Create(String.Format("http://{0}.zendesk.com/{1}/{2}.xml", _site, endpoint, id));
            request.Method = "PUT";
            request.Credentials = new NetworkCredential(_username, _password);
            request.ContentType = "application/xml";
            request.ContentLength = bytes.Length;
            request.Timeout = 10000;

            try
            {
                using (var requestStream = request.GetRequestStream())
                {
                    requestStream.Write(bytes, 0, bytes.Length);
                    requestStream.Close();
                }
                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    var responseCode = response.GetResponseHeader("Status");
                    // Kanskje vi ikke trenger å gjøre noe her...
                }

            }
            catch (Exception ex)
            {
            }

        }

        private void ProcessPostRequest<T>(string endpoint, T value)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(Serialize(value));


            var request = (HttpWebRequest)WebRequest.Create(String.Format("http://{0}.zendesk.com/{1}.xml", _site, endpoint));
            request.Method = "POST";
            request.Credentials = new NetworkCredential(_username, _password);
            request.ContentType = "application/xml";
            request.ContentLength = bytes.Length;
            request.Timeout = 10000;

            try
            {
                using (var requestStream = request.GetRequestStream())
                {
                    requestStream.Write(bytes, 0, bytes.Length);
                    requestStream.Close();
                }
                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    // Kanskje vi ikke trenger å gjøre noe her...
                }

            }
            catch (Exception ex)
            {
            }

        }

        private static string Serialize<T>(T value)
        {
            var settings = new XmlWriterSettings
            {
                Indent = false,
                NewLineHandling = NewLineHandling.None,
                OmitXmlDeclaration = true
            };

            var namespaces = new XmlSerializerNamespaces();
            namespaces.Add(String.Empty, String.Empty);

            var xmlSerializer = new XmlSerializer(typeof(Comment));

            var xml = new StringBuilder();
            using (XmlWriter writer = XmlWriter.Create(xml, settings))
            {
                xmlSerializer.Serialize(writer, value, namespaces);
            }

            return xml.ToString();
        }

        public static void FixNilAttributeName(XmlNode @this)
        {
            if (@this.HasChildNodes)
            {
                foreach (XmlNode childNode in @this.ChildNodes)
                {
                    FixNilAttributeName(childNode);
                }
            }

            if (@this.Attributes == null)
                return;

            XmlAttribute nilAttribute = @this.Attributes["nil"];
            if (nilAttribute == null)
            {
                return;
            }

            XmlAttribute newNil = @this.OwnerDocument.CreateAttribute("xsi", "nil", "http://www.w3.org/2001/XMLSchema-instance");
            newNil.Value = nilAttribute.Value;
            @this.Attributes.Remove(nilAttribute);
            @this.Attributes.Append(newNil);
        }
    }
}